package com.scs.application.modules.dept.util;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.scs.application.core.exception.BusinessException;
import com.scs.application.core.utils.BeanUtils;
import com.scs.application.core.utils.DateUtils;
import com.scs.application.core.utils.StringUtils;
import com.scs.application.core.utils.UtilNum;
import com.scs.application.modules.cfg.utils.ParameterUtils;
import com.scs.application.modules.dept.dto.BudgetCheckItemDTO;
import com.scs.application.modules.dept.dto.BudgetDTO;
import com.scs.application.modules.dept.entity.ApplyItem;
import com.scs.application.modules.dept.entity.Budget;
import com.scs.application.modules.dept.entity.BudgetItem;
import com.scs.application.modules.dept.entity.BudgetMatr;
import com.scs.application.modules.dept.service.BudgetItemService;
import com.scs.application.modules.dept.service.BudgetMatrService;
import com.scs.application.modules.dept.service.BudgetService;
import com.scs.application.modules.finance.entity.Bill;
import com.scs.application.modules.finance.entity.BillItem;
import com.scs.application.modules.finance.service.BillItemService;
import com.scs.application.modules.finance.service.BillService;
import com.scs.application.modules.wm.entity.PickItem;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
* @Description： 科室预算工具类
*/
@Slf4j
@Component
public class UtilBudget {
    private static final Logger logger = LoggerFactory.getLogger(UtilBudget.class);
    @Autowired
    private BudgetMatrService budgetMatrService;
    @Autowired
    private BudgetItemService budgetItemService;
    @Autowired
    private BudgetService budgetService;
    @Autowired
    private BillService billService;
    @Autowired
    private BillItemService billItemService;

    private static UtilBudget services;

    @PostConstruct
    public void init() {
        services = this;
        services.budgetMatrService = this.budgetMatrService;
        services.budgetItemService = this.budgetItemService;
        services.budgetService = this.budgetService;
        services.billService = this.billService;
        services.billItemService = this.billItemService;
    }

    /**获取 上年限额限量  上月限额限量 本月消耗额 消耗量
     * @param year
     * @param month
     * @param deptId
     * @param startDate
     * @param endDate
     */
    public static BudgetDTO getQtyAmount(int year, int month, String deptId, Date startDate, Date endDate,boolean flagHospital) {
        BudgetDTO  budgetDTO = new BudgetDTO();

        //上年限额限量
        QueryWrapper queryWrapper = Wrappers.<Budget>query().eq("period_year",year-1).eq("flow_status","end");
        if (flagHospital) {//全院限制
            queryWrapper.eq("flag_hospital","1");
        }else  { //非全院限制，查询对应科室的
            queryWrapper.ne("flag_hospital","1");
            queryWrapper.eq("dept_id",deptId);
        }
        List<Budget> budgetLaseYearList =  UtilBudget.services.budgetService.list(queryWrapper);

        if (budgetLaseYearList != null && budgetLaseYearList.size() > 0){
            budgetDTO.setLastYearAmount(budgetLaseYearList.stream().filter(tmp -> tmp.getAmount() != null).mapToDouble(Budget::getAmount).sum());
            budgetDTO.setLastYearQty(budgetLaseYearList.stream().filter(tmp -> tmp.getQty() != null).mapToDouble(Budget::getQty).sum());
        }

        //上月限额限量
        int year2 = year,month2 = month -1;
        //上一月为去年
        if (month == 0 ){
            year2--;
            month2 = 12;
        }
        QueryWrapper queryWrapperLastMonth = Wrappers.<Budget>query().eq("period_year",year-1).eq("period_month",month2).eq("flow_status","end");
        if (flagHospital) {//全院限制
            queryWrapperLastMonth.eq("flag_hospital","1");
        }else  { //非全院限制，查询对应科室的
            queryWrapperLastMonth.ne("flag_hospital","1");
            queryWrapperLastMonth.eq("dept_id",deptId);
        }

        Budget budgetLastMonth = UtilBudget.services.budgetService.getOne(queryWrapperLastMonth);
        if (budgetLastMonth != null) {
            budgetDTO.setLastMonthAmount(budgetLastMonth.getAmount());
            budgetDTO.setLastMonthQty(budgetLastMonth.getQty());
        }

        //本月消耗额消耗量
        QueryWrapper queryWrapperBill = Wrappers.<Bill>query().between("bus_date", startDate,endDate);
        //非全院限制时，筛选填报科室
        if (!flagHospital) {
            queryWrapperBill.eq("dept_id",deptId);
        }
        List<Bill> billList = UtilBudget.services.billService.list(queryWrapperBill);

        if (billList != null && billList.size() > 0){

            /*if (billNoRedList != null && billNoRedList.size() > 0) {
                List<String> billIdList = billNoRedList.stream().map(Bill::getId).collect(Collectors.toList());
                List<BillItem> billItemList = UtilBudget.services.billItemService.list(Wrappers.<BillItem>query()
                        .in("bill_id",billIdList)
                );
                billAmount=billItemList.stream().mapToDouble(BillItem::getAmount).sum();
                billQty=billItemList.stream().mapToDouble(BillItem::getSkuQty).sum();
            }

            //冲红账单 科室退货入库、科室消耗冲红入库、病人消耗冲红
            double billRedAmount = 0,billRedQty=0;
            List<Bill> billRedList = billList.stream().filter(billTmp -> WorkOrderType.BUSTYPE_RED_ALL.contains(billTmp.getRefBusType())).collect(Collectors.toList());
            if (billRedList != null && billRedList.size() > 0) {
                List<String> billIdList = billRedList.stream().map(Bill::getId).collect(Collectors.toList());
                List<BillItem> billItemList = UtilBudget.services.billItemService.list(Wrappers.<BillItem>query()
                        .in("bill_id",billIdList)
                );
                billRedAmount=billItemList.stream().mapToDouble(BillItem::getAmount).sum();
                billRedQty=billItemList.stream().mapToDouble(BillItem::getSkuQty).sum();
            }*/

            //非冲红账单
            double billAmount = 0,billQty=0;

            //冲红账单
            double billRedAmount = 0,billRedQty=0;

            List<String> billIdList = billList.stream().map(Bill::getId).collect(Collectors.toList());
            List<BillItem> billItemList = UtilBudget.services.billItemService.list(Wrappers.<BillItem>query()
                    .in("bill_id",billIdList)
            );
            if (billItemList != null && billItemList.size() > 0) {
                for(BillItem billItem : billItemList) {
                    if (billItem.getFlagRed()) {
                        billRedAmount = UtilNum.sum(billRedAmount,billItem.getAmount());
                        billRedQty = UtilNum.sum(billRedQty,billItem.getSkuQty());
                    }else {
                        billAmount = UtilNum.sub(billAmount,billItem.getAmount());
                        billQty = UtilNum.sub(billQty,billItem.getSkuQty());
                    }
                }
            }

            budgetDTO.setAmountUseMonth(UtilNum.sub(billAmount,billRedAmount));
            budgetDTO.setQtyUseMonth(UtilNum.sub(billQty,billRedQty));
        }

        return budgetDTO;
    }


    /**产生记账单后，更新该科室月度消耗量、消耗额
     * 未找到则忽略
     * @param deptId
     */
    public static void updateBudget(String deptId) {
        if(StringUtils.isBlank(deptId)) return;
        synchronized (deptId.intern()){
            String DateNowStr = DateUtils.nowDate();
            String year = DateNowStr.split("-")[0];
            String month = DateNowStr.split("-")[1];

            //该科室本月预算
            List<Budget> budgetList = UtilBudget.services.budgetService.list(Wrappers.<Budget>query()
                    .eq("dept_id",deptId)
                    .eq("period_year",year)
                    .eq("period_month",month)
                    .ne("flag_hospital","1")
            );
            if (budgetList != null && budgetList.size() > 0) {
                budgetList.stream().forEach(budget -> {
                    _updateBudget(budget);
                });
            }


            //全院本月预算
            budgetList= UtilBudget.services.budgetService.list(Wrappers.<Budget>query()
                    .eq("period_year",year)
                    .eq("period_month",month)
                    .eq("flag_hospital","1")
            );
            if (budgetList != null && budgetList.size() > 0) {
                budgetList.stream().forEach(budget -> {
                    _updateBudget(budget);
                });
            }
        }
    }

    //更新预算消耗
    private static  void _updateBudget(Budget budgetMonth) {

        //本月消耗额消耗量
        QueryWrapper queryWrapperBill = Wrappers.<Bill>query().between("bus_date", budgetMonth.getStartDate(),budgetMonth.getEndDate());
        //非全院预算时，筛选填报科室
        if (!budgetMonth.getFlagHospital()) {
            queryWrapperBill.eq("dept_id",budgetMonth.getDeptId());
        }


        //本月消耗额消耗量
        List<Bill> billList = UtilBudget.services.billService.list(queryWrapperBill );

        if (billList != null && billList.size() > 0){

            //非冲红账单
            double billAmount = 0,billQty=0;

            //冲红账单
            double billRedAmount = 0,billRedQty=0;

            List<String> billIdListAll = billList.stream().map(Bill::getId).collect(Collectors.toList());
            List<BillItem> billItemList = UtilBudget.services.billItemService.list(Wrappers.<BillItem>query()
                    .in("bill_id",billIdListAll)
            );
            if (billItemList != null && billItemList.size() > 0) {
                for(BillItem billItem : billItemList) {
                    if (billItem.getFlagRed()) {
                        billRedAmount = UtilNum.sum(billRedAmount,billItem.getAmount());
                        billRedQty = UtilNum.sum(billRedQty,billItem.getSkuQty());
                    }else {
                        billAmount = UtilNum.sub(billAmount,billItem.getAmount());
                        billQty = UtilNum.sub(billQty,billItem.getSkuQty());
                    }
                }
            }
            budgetMonth.setAmountUseMonth(billAmount-billRedAmount);
            budgetMonth.setQtyUseMonth(billQty-billRedQty);
            UtilBudget.services.budgetService.updateCascadeById(budgetMonth);


            //更新预算明细本月消耗金额
            List<BudgetItem> budgetItemtList = UtilBudget.services.budgetItemService.list(Wrappers.<BudgetItem>query()
                    .in("budget_id",budgetMonth.getId()));

            if (budgetItemtList != null && budgetItemtList.size() > 0) {
                List<String> billIdList = billList.stream().map(Bill::getId).collect(Collectors.toList());
                List<BillItem> billItemAllList = UtilBudget.services.billItemService.list(Wrappers.<BillItem>query().in("bill_id", billIdList));

                List<String> finalBillItemIdNoRedList = billItemList.stream().filter(tmp -> !tmp.getFlagRed()).map(BillItem::getId).collect(Collectors.toList());
                List<String> finalBillItemIdRedList = billItemList.stream().filter(tmp -> tmp.getFlagRed()).map(BillItem::getId).collect(Collectors.toList());
                budgetItemtList.stream().forEach(budgetItem -> {
                    //该预算明细所限定的耗材
                    List<BudgetMatr> budgetMatrList = UtilBudget.services.budgetMatrService.list(Wrappers.<BudgetMatr>query()
                            .in("budget_item_id",budgetItem.getId()));
                    if (budgetMatrList != null && budgetMatrList.size() > 0) {
                        List<String> matrIdList = budgetMatrList.stream().map(BudgetMatr::getMatrId).collect(Collectors.toList());
                        //包含预算明细限定耗材的非冲红记账明细
                        List<BillItem> billItemNoRedList2 = billItemAllList.stream().filter(
                                billItem -> matrIdList.contains(billItem.getMatrId()) && finalBillItemIdNoRedList.contains(billItem.getId())
                        ).collect(Collectors.toList());

                        //包含预算明细限定耗材的冲红记账明细
                        List<BillItem> billItemRedList2 = billItemAllList.stream().filter(
                                billItem -> matrIdList.contains(billItem.getMatrId()) && finalBillItemIdRedList.contains(billItem.getId())
                        ).collect(Collectors.toList());

                        double billAmountItem = 0,billQtyItem=0;
                        double billRedAmountItem = 0,billRedQtyItem=0;
                        if (billItemNoRedList2 != null && billItemNoRedList2.size() > 0) {
                            billAmountItem=billItemNoRedList2.stream().mapToDouble(BillItem::getAmount).sum();
                            billQtyItem=billItemNoRedList2.stream().mapToDouble(BillItem::getSkuQty).sum();
                        }

                        if (billItemRedList2 != null && billItemRedList2.size() > 0) {
                            billRedAmountItem=billItemRedList2.stream().mapToDouble(BillItem::getAmount).sum();
                            billRedQtyItem=billItemRedList2.stream().mapToDouble(BillItem::getSkuQty).sum();
                        }
                        budgetItem.setAmountUseMonth(billAmountItem-billRedAmountItem).setQtyUseMonth(billQtyItem-billRedQtyItem);
                        UtilBudget.services.budgetItemService.updateById(budgetItem);
                    }
                });
            }

        }
    }


    /**获取  登录科室当月预算
     * @param deptId
     */
    public static Budget getBudgetForLoginUser(String deptId) {
        if(StringUtils.isBlank(deptId)) return null;
        String DateNowStr = DateUtils.nowDate();
        String year = DateNowStr.split("-")[0];
        String month = DateNowStr.split("-")[1];
        return UtilBudget.services.budgetService.getOne(Wrappers.<Budget>query()
                .eq("dept_id",deptId)
                .eq("period_year",year)
                .eq("period_month",month)
                .eq("flow_status","end")
                .ne("flag_hospital","1")
        );
    }



    /**检查预算控制
     * budget_control_type  0或null或参数不存在：不做任何处理，1:限制科室请领需求， 其他：待扩展
     * @param deptId
     */
    public static void checkBudgetControl(String deptId, List<?> checkItems) {
        if(StringUtils.isBlank(deptId)) return;
        if (checkItems == null || checkItems.size() < 1) return;
        String budgetControlType = ParameterUtils.spd.getBudgetControlType();
        if (StringUtils.isBlank(budgetControlType) || "0".equals(budgetControlType)) return;
        String DateNowStr = DateUtils.nowDate();
        String year = DateNowStr.split("-")[0];
        String month = DateNowStr.split("-")[1];

        switch (budgetControlType){
            case "1": //1:限制科室请领需求、配送。调拨
                //当月科室预算
                Budget budget = UtilBudget.services.budgetService.getOne(Wrappers.<Budget>query()
                        .eq("dept_id",deptId)
                        .eq("period_year",year)
                        .eq("period_month",month)
                        .eq("flow_status","end")
                        .ne("flag_hospital","1")
                );
                if (budget != null){
                    _checkBudgetControl(budget,checkItems);
                }

                //当月全院预算
                budget = UtilBudget.services.budgetService.getOne(Wrappers.<Budget>query()
                        .eq("period_year",year)
                        .eq("period_month",month)
                        .eq("flow_status","end")
                        .eq("flag_hospital","1")
                );
                if (budget != null){
                    _checkBudgetControl(budget,checkItems);
                }
                break;
        }
    }


    /**
    * @Description： 校验预算耗材限制
    */
    private static void _checkBudgetControl(Budget budget, List<?> checkObjects) {
        //预算明细
        List<BudgetItem> budgetItemtList = UtilBudget.services.budgetItemService.list(Wrappers.<BudgetItem>query()
                .in("budget_id",budget.getId()));
        if (budgetItemtList == null || budgetItemtList.size() < 1) return;

        //该预算明细所限定的耗材
        List<BudgetMatr> budgetMatrList = UtilBudget.services.budgetMatrService.list(Wrappers.<BudgetMatr>query()
                .in("budget_id",budget.getId()));
        if (budgetMatrList == null || budgetMatrList.size() < 1) return;
        String msgPre;
        if (budget.getFlagHospital()) {
            msgPre="全院";
        }else {
            msgPre="科室";
        }

        List<BudgetCheckItemDTO> checkItemDTOList = new ArrayList<BudgetCheckItemDTO>();
        if (checkObjects.get(0) instanceof ApplyItem) { //请领需求
            ((List<ApplyItem>)checkObjects).stream().forEach(item -> {
                BudgetCheckItemDTO budgetCheckItemDTO = new BudgetCheckItemDTO();
                budgetCheckItemDTO.setMatrId(item.getMatrId()).setSkuQty(item.getSkuQty()).setAmount(item.getAmount());
                checkItemDTOList.add(budgetCheckItemDTO);
            });
            BeanUtils.copyProperties(checkItemDTOList,(List<ApplyItem>)checkObjects);
        } else  if (checkObjects.get(0) instanceof PickItem) { //配送
            ((List<PickItem>)checkObjects).stream().forEach(item -> {
                BudgetCheckItemDTO budgetCheckItemDTO = new BudgetCheckItemDTO();
                budgetCheckItemDTO.setMatrId(item.getMatrId()).setSkuQty(item.getSkuQty()).setAmount(item.getAmount());
                checkItemDTOList.add(budgetCheckItemDTO);
            });
        } else  if (checkObjects.get(0) instanceof PickItem) { //配送
            ((List<PickItem>)checkObjects).stream().forEach(item -> {
                BudgetCheckItemDTO budgetCheckItemDTO = new BudgetCheckItemDTO();
                budgetCheckItemDTO.setMatrId(item.getMatrId()).setSkuQty(item.getSkuQty()).setAmount(item.getAmount());
                checkItemDTOList.add(budgetCheckItemDTO);
            });
        }

        //1、预算明细
        budgetItemtList.stream().forEach(budgetItem -> {
            //2、预算明细所限制的耗材
            Map<String, BudgetMatr> budgetMatrMap =  budgetMatrList.stream().filter(budgetMatr -> budgetMatr.getBudgetItemId().equals(budgetItem.getId()))
                    .collect(Collectors.toMap(BudgetMatr::getMatrId, Function.identity()));

            //3、请领明细的耗材在该预算明细里面的
            List<BudgetCheckItemDTO> checkItemDTOListFind = checkItemDTOList.stream().filter(checkItemDTO -> budgetMatrMap.containsKey(checkItemDTO.getMatrId())).collect(Collectors.toList());
            if (checkItemDTOListFind != null && checkItemDTOListFind.size() > 0) {
                double amountUseNew=checkItemDTOListFind.stream().mapToDouble(BudgetCheckItemDTO::getAmount).sum();
                double qtyUseNew=checkItemDTOListFind.stream().mapToDouble(BudgetCheckItemDTO::getSkuQty).sum();
                double amountUserMonth =UtilNum.getDouble(budgetItem.getAmountUseMonth());
                double qtyUseMonth =UtilNum.getDouble(budgetItem.getQtyUseMonth());
                double amountBeyond = amountUserMonth+amountUseNew-budgetItem.getAmount();
                double qtyeyond =qtyUseMonth+qtyUseNew-budgetItem.getQty();
                //限额判断
                if (budgetItem.getAmount() > 0 && amountBeyond > 0) {
                    throw  new BusinessException("已超过%s预算限额限制<br>%s本月限额(%s)<br>%s本月已消耗额(%s)<br>当前需追加消耗额(%s)，超出:%s"
                            ,msgPre,msgPre,budgetItem.getAmount(),msgPre,amountUserMonth, UtilNum.getDouble(amountUseNew),UtilNum.getDouble(amountBeyond));
                }

                //限量判断
                if (budgetItem.getQty() > 0 && qtyeyond > 0) {
                    throw  new BusinessException("已超过%s预算限量限制<br>%s本月限量(%s)<br>%s本月已消耗量(%s)<br>当前需追加消耗量(%s)，超出:%s"
                            ,msgPre,msgPre,budgetItem.getQty(),msgPre,qtyUseMonth,UtilNum.getDouble(qtyUseNew),UtilNum.getDouble(qtyeyond));
                }

            }
        });
    }
}
